package com.vecspace.database.dao;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Clob;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import javax.persistence.Column;
import com.vecspace.data.model.IPagination;
import com.vecspace.data.model.RowMapper;
import com.vecspace.data.numerical.HMoneyWanYuan;
import com.vecspace.data.numerical.HMoneyYuan;
import com.vecspace.data.numerical.HNumber;
import com.vecspace.data.numerical.HPercent;
import com.vecspace.database.model.*;
import com.vecspace.exception.HException;
import com.vecspace.utils.Convert;

import comvecspace.data.model.sql.SqlDataType;
/**
 * @author：
 */
public class HibernateBaseDaoImpl implements DaoHelper {
	private HibernateDaoHelper hibernateDaoHelper;// 实际HIBERNATE数据库操作类对象;
	private JdbcDaoHelper jdbcDaoHelper;// 实际JDBC数据库操作类对象;

	/**
	 * 通过hibernate对象进行对象保存
	 */
	@Override
	public void insertObject(Object obj) throws Exception {
		this.hibernateDaoHelper.saveObject(obj);
	}


	/**
	 * 通过hibernate对象进行对象更新
	 */
	@Override
	public void updateObject(Object obj) throws Exception {
		this.hibernateDaoHelper.saveObject(obj);
	}

	/**
	 * 通过hibernate对象进行对象更新或保存
	 */
	@Override
	public void saveOrUpdateObject(Object obj) {
		this.hibernateDaoHelper.saveOrUpdateObject(obj);
	}

	@Override
	public <T> T queryObject(Class<T> clazz, Serializable id) throws Exception {
		return hibernateDaoHelper.queryObject(clazz, id);
	}

	@Override
	public <T> T queryObject(String hql, Object[] params) throws Exception {
		List<T> list = hibernateDaoHelper.queryObjectList(hql, params);
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		else {
			return null;
		}
	}

	@Override
	public Object[] queryDataRow(String sql, Object[] params) throws Exception {
		List<Object[]> list = this.queryDataRowList(sql, params);
		if (list == null || list.size() == 0) {
			return null;
		}
		return (Object[]) list.get(0);
	}

	/*
	 * public Object queryDataRow(Object id)throws Exception { return null; }
	 */
	@Override
	public void deleteObject(Class<?> clazz, Serializable id) throws Exception {
		Object obj = this.hibernateDaoHelper.queryObject(clazz, id);
		hibernateDaoHelper.deleteObject(obj);
	}

	@Override
	public void deleteObject(Object obj) throws Exception {
		hibernateDaoHelper.deleteObject(obj);
	}

	@Override
	public int deleteDataRowBySql(String sql, Object[] params) throws Exception {
		return this.jdbcDaoHelper.updateDataRowBySql(sql, params);
	}

	@Override
	public void deleteObjects(List<?> list) throws Exception {
		hibernateDaoHelper.deleteObject(list);
	}

	/**
	 * 需要进行分页的查询，无查询条件
	 */
	@Override
	public <T> QueryResult<T> queryObjectListByPage(String hql, IPagination pagination) throws Exception {
		return hibernateDaoHelper.queryObjectListByPage(hql, pagination);
	}

	/**
	 * 无需要进行分页的查询，无查询条件
	 */
	@Override
	public <T> List<T> queryObjectList(String hql) throws Exception {
		return hibernateDaoHelper.queryObjectList(hql);
	}

	/**
	 * 需要进行分页的查询，有查询条件
	 */
	@Override
	public <T> QueryResult<T> queryObjectListByPage(String hql, String[] paramNames, Object[] params, IPagination pagination) throws Exception {
		return hibernateDaoHelper.queryObjectListByPage(hql, paramNames, params, pagination);
	}

	@Override
	public <T> List<T> queryObjectList(String hql, String[] paramNames, Object[] params)
			throws Exception {
		return hibernateDaoHelper.queryObjectList(hql, paramNames, params);
	}

	@Override
	public <T> List<T>  queryObjectList(String hql, Object[] params) throws Exception {
		return hibernateDaoHelper.queryObjectList(hql, params);
	}

	@Override
	public <T> QueryResult<T> queryObjectListByPage(String hql, Object[] params, IPagination pagination) throws Exception {
		return hibernateDaoHelper.queryObjectListByPage(hql, params, pagination);
	}

	/**
	 * 方法功能描述：根据一条完整的标准sql语句查询出多个Hibernate对象(分页查询)。 调用举例：getDataRowByHSql(
	 * "select b.* from b left outer join a on a.pid=b.id where b.id=?",new
	 * String[]{"100001"});
	 * 
	 * @param sql
	 *            一个完整的标准sql语句
	 * @param params
	 *            sql中对应的参数值
	 * @return
	 * @throws Exception
	 * @author wanghua
	 */
	@Override
	public <T> QueryResult<T> queryObjectPageBySql(Class<T> clazz, String sql, Object[] params, IPagination pagination) throws Exception {
		return hibernateDaoHelper.queryDataRowPageBySql(clazz, sql, params, pagination);
	}

	/**
	 * 方法功能描述：根据一条完整的标准sql语句查询出多个Hibernate对象。 调用举例：getDataRowByHSql(
	 * "select b.* from b left outer join a on a.pid=b.id where b.id=?",new
	 * String[]{"100001"});
	 * 
	 * @param sql
	 *            一个完整的标准sql语句
	 * @param params
	 *            sql中对应的参数值
	 * @return
	 * @throws Exception
	 * @author wanghua
	 */
	@Override
	public <T> List<T> queryObjectListBySql(Class<T> clazz, String sql, Object[] params) throws Exception {
		List<Map<String, Object>> rows = this.queryDataRowMapList(sql, params);
		List<T> objList = new ArrayList<>();
		for(var row : rows) {
			T obj = this.transform2Entity(clazz, row);
			objList.add(obj);
		}
		return objList;
		//return hibernateDaoHelper.queryObjectListBySql(clazz, sql, params);
	}
	
	/**
	 * 方法功能描述：根据一条完整的标准sql语句查询出一个Hibernate对象。 调用举例：getDataRowByHSql(
	 * "select a.* from a left outer join b on a.id=b.pid where b.id=?",new
	 * String[]{"100001"});
	 * 
	 * @param sql
	 *            一个完整的标准sql语句
	 * @param params
	 *            sql中对应的参数值
	 * @return
	 * @throws Exception
	 * @author wanghua
	 */
	@Override
	public <T> T queryObjectBySql(Class<T> clazz, String sql, Object[] params) throws Exception {
		Map<String, Object> row = this.queryDataRowMap(sql, params);
		if(row == null)return null;
		T obj = this.transform2Entity(clazz, row);
		return obj;
		//return hibernateDaoHelper.queryObjectBySql(clazz, sql, params);
	}
	

	/*---------------------------------------------------------------------------------------------------------------*/

	@Override
	public QueryResult<Object[]> queryDataRowListByPage(String sql, IPagination pagination) throws Exception {
		return jdbcDaoHelper.queryDataRowPageBySql(sql, pagination);
	}

	@Override
	public QueryResult<Object[]> queryDataRowListByPage(String sql, Object[] params, IPagination pagination) throws Exception {
		return jdbcDaoHelper.queryDataRowPageBySql(sql, params, pagination);
	}

	/**
	 * 无需要进行分页的查询，无查询条件
	 */
	@Override
	public List<Object[]> queryDataRowList(String sql) throws Exception {
		return jdbcDaoHelper.queryDataRowsBySql(sql);
	}

	@Override
	public List<Object[]> queryDataRowList(String sql, Object[] params) throws Exception {
		return jdbcDaoHelper.queryDataRowsBySql(sql, params);
	}

	@Override
	public List<Map<String,Object>> queryDataRowMapList(String sql, Object[] params) throws Exception {
		return jdbcDaoHelper.queryDataRowMapListBySql(sql, params);
	}
	
	public List<Long> batchUpdateReturnKey(String sql, List<Object[]> params) throws Exception{
		return jdbcDaoHelper.batchUpdateReturnKey(sql, params);
	}
	
	/**获取一行记录
	 * @param sql
	 * @param params
	 * @return
	 * @throws Exception 
	 * @author wanghua
	 */
	@Override
	public Map<String,Object> queryDataRowMap(String sql,Object[] params) throws Exception{
		List<Map<String,Object>> list = jdbcDaoHelper.queryDataRowMapListBySql(sql, params);
		if(list == null || list.size() == 0) {
			return null;
		}
		else {
			return list.get(0);
		}
	}
	

	public HibernateDaoHelper queryHibernateDaoHelper() {
		return hibernateDaoHelper;
	}

	public void setHibernateDaoHelper(HibernateDaoHelper hibernateDaoHelper) {
		this.hibernateDaoHelper = hibernateDaoHelper;
	}

	public JdbcDaoHelper queryJdbcDaoHelper() {
		return jdbcDaoHelper;
	}

	public void setJdbcDaoHelper(JdbcDaoHelper jdbcDaoHelper) {
		this.jdbcDaoHelper = jdbcDaoHelper;
	}

	@Override
	public List<Map<String,Object>> queryDataRowMapList(String sql) throws Exception {
		return this.jdbcDaoHelper.queryDataRowMapListBySql(sql);
	}
	

	@Override
	public QueryResult<Map<String,Object>> queryDataRowMapListByPage(String sql, Object [] params, IPagination pagination) throws Exception {
		return this.jdbcDaoHelper.queryDataRowMapPageBySql(sql, params, pagination);
	}

	@Override
	public QueryResult<Map<String,Object>> queryDataRowMapListByPage(String sql, IPagination pagination) throws Exception {
		return this.jdbcDaoHelper.queryDataRowMapPageBySql(sql, pagination);
	}

	@Override
	public int[] batchUpdateDataRowBySql(String sql, List<Object[]> batchArgs)
			throws Exception {
		return this.jdbcDaoHelper.batchUpdateDataRowBySql(sql, batchArgs);
	}

	@Override
	public int updateDataRowBySql(String sql, Object[] params) throws Exception {
		return this.jdbcDaoHelper.updateDataRowBySql(sql, params);
	}
	
	@Override
	public int insertDataRowBySql(String sql, Object[] params) throws Exception {
		return this.jdbcDaoHelper.updateDataRowBySql(sql, params);
	}

	@Override
	public int callProcedure(String procedureName, List<String[]> params)
			throws Exception {
		return this.jdbcDaoHelper.callProcedure(procedureName, params);
	}

	/**
	 * 返回表字段的数据类型 Map<String, Integer> --> Map<字段名（小写）, java.sql.Types>
	 * 
	 * @param tableName
	 * @return
	 * @throws SQLException
	 * @author wanghua
	 */
	@Override
	public Map<String, SqlDataType> queryColumnsType(String tableName)
			throws SQLException {
		return this.jdbcDaoHelper.queryColumnsType(tableName);
	}

	@Override
	public DataTable queryDataTable(String sql, Object[] params, int pageIndex,
			int pageSize) throws Exception {
		return this.jdbcDaoHelper
				.queryDataTable(sql, params, pageIndex, pageSize);
	}
	
	@Override
	public DataTable queryDataTable(String sql, Object[] params) throws Exception {
		return this.jdbcDaoHelper.queryDataTable(sql, params);
	}
	
	@Override
	public Object queryValue(String sql) throws Exception{
		Object[] row = this.queryDataRow(sql, null);
		if(row == null) {
			return null;
		}
		return row[0];
	}
	
	@Override
	 public Object queryValue(String sql, Object[] params) throws Exception{
		Object[] row = this.queryDataRow(sql, params);
		if(row == null) {
			return null;
		}
		return row[0];
	}
	  
	@Override
	public String queryString(String sql) throws Exception{
		Object obj = queryValue(sql);
		if(obj == null) {
			return null;
		}
		else {
			return obj.toString();
		}
	}
	@Override
	public String queryString(String sql, Object[] params) throws Exception{
		Object obj = queryValue(sql, params);
		if(obj == null) {
			return null;
		}
		else {
			return obj.toString();
		}
	}
	  
	@Override
	public int queryInt(String sql) throws Exception{
		Object obj = queryValue(sql);
		if(obj == null) {
			return 0;
		}
		else {
			return Convert.toInt(obj, 0);
		}
	}
	@Override
	public int queryInt(String sql, Object[] params) throws Exception{
		Object obj = queryValue(sql, params);
		if(obj == null) {
			return 0;
		}
		else {
			return Convert.toInt(obj, 0);
		}
	}
	
  
	@Override
	public long queryLong(String sql) throws Exception{
		Object obj = queryValue(sql);
		if(obj == null) {
			return 0;
		}
		else {
			return Convert.toLong(obj, 0L);
		}
	}
	@Override
	public long queryLong(String sql, Object[] params) throws Exception{
		Object obj = queryValue(sql, params);
		if(obj == null) {
			return 0;
		}
		else {
			return Convert.toLong(obj, 0L);
		}
	}
	@Override
	public Clob createClob() throws Exception{
		return this.jdbcDaoHelper.createClob();
	}
	
	@Override
	public void evict(Object obj){
		this.hibernateDaoHelper.evict(obj);
	}
	
	@Override
	public void evictList(List<?> objList){
		this.hibernateDaoHelper.evictList(objList);
	}

	@Override
	public void execute(String ddl) throws Exception{
		this.jdbcDaoHelper.execute(ddl);
	}
	

	@Override
	public <T> QueryResult<T> queryEntities(QuerySQL querySql, IPagination pagination, IDataRowMapper<T> mapper) throws Exception{
		var mapResult = queryEntities(Map.class, querySql, pagination);
		QueryResult<T> queryResult = new QueryResult<>();
		List<T> entities = new ArrayList<T>();
		for(var row : mapResult.getEntities()) {
			T t = mapper.translate((Map<String, Object>)row);
			entities.add(t);
		}
		queryResult.setEntities(entities);
		return queryResult;
	}
	
	@Override
	public <T> QueryResult<T> queryEntities(Class<T> clazz, QuerySQL querySql) throws Exception {
		return this.queryEntitiesAndAggregate(clazz, Void.class, querySql, null);
	}

	@Override
	public <T, R> QueryResultAggregation<T, R> queryEntitiesAndAggregate(Class<T> clazz, Class<R> aggregateClazz, QuerySQL querySql) throws Exception{
		return this.queryEntitiesAndAggregate(clazz, aggregateClazz, querySql, null);
	}


	@Override
	public <T> QueryResult<T> queryEntities(Class<T> clazz, QuerySQL querySql, IPagination pagination) throws Exception{
		return this.queryEntitiesAndAggregate(clazz, Void.class, querySql, pagination);
	}

	@Override
	public <T, R> QueryResultAggregation<T, R> queryEntitiesAndAggregate(Class<T> clazz, Class<R> aggregateClazz, QuerySQL querySql, IPagination pagination) throws Exception {
		String sqlText = "";
		int totalRows = 0;
		Object[] pValues = querySql.getSqlParameterValues();
		if(pagination == null || !pagination.isPaged()) {//不分页查询
			sqlText = querySql.getQuerySqlText();
		}else {//分页查询
			//查询指定分页的sql
			sqlText = querySql.getPageSqlText(pagination.getPageIndex(), pagination.getPageSize());
			//获取总记录数的sql
			String countSql = querySql.getTotalRowsSqlText();
			//从数据库中查询出总记录数
			totalRows = this.queryInt(countSql, pValues);
			pagination.setTotalRows(totalRows);
		}
		
		//执行查询
		List<T> entities = this.queryEntityListBySql(clazz, sqlText, pValues);
		
		//判断是否需要进行聚合查询
		List<R> aggregateEntities = new ArrayList<>();
		if( aggregateClazz != null && !aggregateClazz.equals(Void.class) && !aggregateClazz.equals(void.class) && querySql.hasAggregate()) {
			for(int i=0; i<querySql.getGroupCount(); i++){
				//聚合查询的sql
				String aggregateSqlText = querySql.getAggregateSqlText(i);
				String groupRowKey = "";
				String groupName = querySql.getGroupName(i);
				String groupLabel = querySql.getGroupLabel(i);
				//执行聚合查询
				List<R> aggregateEntities1 = this.queryEntityListBySql(aggregateClazz, aggregateSqlText, pValues);
				if(AggregateEntity.class.isAssignableFrom(aggregateClazz) ){
					for(R entity : aggregateEntities1){
						((AggregateEntity)entity).setAggregateGroupName(groupName);
						((AggregateEntity)entity).setAggregateGroupLabel(groupLabel);
					}
				}else if(Map.class.isAssignableFrom(aggregateClazz) ){
					for(R entity : aggregateEntities1){
						((Map)entity).put("aggregateGroupName", groupName);
						((Map)entity).put("aggregateGroupLabel", groupLabel);
					}
				}
				aggregateEntities.addAll(aggregateEntities1);
			}
		}
		
		QueryResultAggregation<T, R> result = new QueryResultAggregation<T, R>(entities, pagination, aggregateEntities);
		return result;
	}

	@Override
	public <T> List<T> queryEntityListBySql(Class<T> clazz, String sql, Object[] params) throws Exception {
		List<Map<String,Object>> rowList = this.jdbcDaoHelper.queryDataRowMapListBySql(sql, params);
		List<T> entityList = new ArrayList<T>(rowList.size());		
		for(Map<String,Object> dataRow : rowList) {
			T entity = transform2Entity(clazz, dataRow);
			entityList.add(entity);
		}
		return entityList;
	}
	
	/**1、将Map对象转为JavaBean对象（根据JavaBean属性上的Column注解。有Column注解，但map中不存在值，不会抛出异常）<br>
	 * 2、如果 T 为 Map，则直接返回dataRow
	 * @param <T> JavaBean类
	 * @param clazz JavaBean类
	 * @param dataRow Map对象
	 * @return
	 * @throws Exception 
	 * @author wanghua
	 * @reviewer  
	 * @createTime 2021年9月5日 下午9:55:18
	 */
	@SuppressWarnings("unchecked")
	private <T> T transform2Entity(Class<T> clazz, Map<String, Object> dataRow) throws Exception {
		if(Map.class.isAssignableFrom(clazz)) {
			return (T)dataRow;
		}
		if(dataRow == null) return null;
		T entity = clazz.getDeclaredConstructor().newInstance();
		Method[] methods = clazz.getMethods();
		RowMapper rowMapper = new RowMapper(dataRow);
		for(Method method : methods) {
			Column columnAnno = method.getAnnotation(Column.class);//有Column注解的属性
			if(columnAnno != null) {
				String columnName = columnAnno.name();//根据prop属性上的Column注解获取字段名
				Object val0 = dataRow.get(columnName);//根据字段名，从Map中获取值
				if(val0 != null) {//如果值不为null，则对prop进行赋值
					String methodName = method.getName();
					Class<?> propType = method.getReturnType();

					Object propVal = null;
					if(String.class.equals(propType)){ propVal = rowMapper.getString(columnName); }
					else if(Long.class.equals(propType)){ propVal = rowMapper.getLong(columnName); }
					else if(Integer.class.equals(propType)){ propVal = rowMapper.getInteger(columnName); }
					else if(BigDecimal.class.equals(propType)){ propVal = rowMapper.getBigDecimal(columnName); }
					else if(Double.class.equals(propType)){ propVal = rowMapper.getDouble(columnName); }
					else if(Date.class.equals(propType)){ propVal = rowMapper.getDate(columnName); }
					else if(LocalDateTime.class.equals(propType)){ propVal = rowMapper.getLocalDateTime(columnName); }
					else if(LocalDate.class.equals(propType)){ propVal = rowMapper.getLocalDate(columnName); }
					else if(Boolean.class.equals(propType)){ propVal = rowMapper.getBoolean(columnName); }
					else if(HNumber.class.equals(propType)){ propVal = rowMapper.getHNumber(columnName); }
					else if(HMoneyYuan.class.equals(propType)){ propVal = rowMapper.getMoneyYuan(columnName); }
					else if(HMoneyWanYuan.class.equals(propType)){ propVal = rowMapper.getMoneyWanYuan(columnName); }
					else if(HPercent.class.equals(propType)){ propVal = rowMapper.getPercent(columnName); }
					else if(int.class.equals(propType)){ propVal = rowMapper.get_int(columnName); }
					else if(long.class.equals(propType)){ propVal = rowMapper.get_long(columnName); }
					
					if(methodName.startsWith("set")) {
						method.invoke(entity, propVal);
					}else {
						String propSetMethodName = "";
						if (methodName.startsWith("get")) {
							propSetMethodName = "set" + methodName.substring(3);
						} else if (methodName.startsWith("is")) {
							propSetMethodName = "set" + methodName.substring(2);
						} else
							throw new HException("unexcept method name：" + methodName);
						if(propSetMethodName != null && propSetMethodName.length() > 0) {
							for (Method method2 : methods) {
								if (method2.getName().equals(propSetMethodName) && method2.getParameterCount() == 1) {
									try {
										method2.invoke(entity, propVal);
									}catch(Exception ex2) {
										throw ex2;
									}
									break;
								}
							}
						}
					}
				}
			}
		}
		return entity;
	}
}
